package com.uuah.server.transport;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.uuah.utils.MapUtils;
import com.uuah.utils.ObjectUtils;

public class MessagePropertiesContext implements Serializable {
	protected Map scopedMap;
	protected Set keySet;
	private PropertyScope defaultScope = PropertyScope.SESSION;

	public MessagePropertiesContext() {
		keySet = new TreeSet();
		scopedMap = new TreeMap(new PropertyScope.ScopeComparator());

		scopedMap.put(PropertyScope.INVOCATION, new HashMap(6));
		scopedMap.put(PropertyScope.REQUEST, new HashMap(6));
		scopedMap.put(PropertyScope.SESSION, new HashMap(6));

	}

	public MessagePropertiesContext(PropertyScope defaultScope) {
		this();
		checkScopeForWriteAccess(defaultScope);
		this.defaultScope = defaultScope;
	}

	protected Map getScopedProperties(PropertyScope scope) {
		Map map = (Map) scopedMap.get(scope);
		if (map == null) {
			throw new IllegalArgumentException("Scope not registered: " + scope);
		}
		return map;
	}

	void registerInvocationProperties(Map properties) {
		if (properties != null) {
			getScopedProperties(PropertyScope.INVOCATION).putAll(properties);
			keySet.addAll(properties.keySet());
		}
	}

	public PropertyScope getDefaultScope() {
		return defaultScope;
	}

	void addRequestProperties(Map properties) {
		if (properties != null) {
			getScopedProperties(PropertyScope.REQUEST).putAll(properties);
			keySet.addAll(properties.keySet());
		}
	}

	void registerSessionProperties(Map properties) {
		if (properties != null) {
			getScopedProperties(PropertyScope.SESSION).putAll(properties);
			keySet.addAll(properties.keySet());
		}
	}

	public Object getProperty(String key) {
		Object value = null;
		for (Iterator iterator = scopedMap.values().iterator(); iterator
				.hasNext();) {
			Map props = (Map) iterator.next();
			value = props.get(key);
			if (value != null) {
				break;
			}
		}

		return value;
	}

	public Object getProperty(String key, PropertyScope scope) {
		Map props = getScopedProperties(scope);
		return props.get(key);
	}

	public void clearProperties() {
		Map props = getScopedProperties(PropertyScope.INVOCATION);
		keySet.removeAll(props.keySet());
		props.clear();
		props = getScopedProperties(PropertyScope.REQUEST);
		keySet.removeAll(props.keySet());
		props.clear();
		props = getScopedProperties(PropertyScope.SESSION);
		keySet.removeAll(props.keySet());
		props.clear();
	}

	public void clearProperties(PropertyScope scope) {
		checkScopeForWriteAccess(scope);
		Map props = getScopedProperties(scope);
		keySet.removeAll(props.keySet());
		props.clear();
	}

	public Object removeProperty(String key) {
		Object value = getScopedProperties(PropertyScope.INVOCATION)
				.remove(key);
		if (value == null) {
			value = getScopedProperties(PropertyScope.REQUEST).remove(key);
		}
		if (value == null) {
			value = getScopedProperties(PropertyScope.SESSION).remove(key);
		}
		if (value != null) {
			keySet.remove(key);
		}
		return value;
	}

	public void setProperty(String key, Object value) {
		getScopedProperties(defaultScope).put(key, value);
		keySet.add(key);
	}

	public void setProperty(String key, Object value, PropertyScope scope) {
		checkScopeForWriteAccess(scope);
		getScopedProperties(scope).put(key, value);
		keySet.add(key);
	}

	public Set getPropertyNames() {
		return Collections.unmodifiableSet(keySet);
	}

	public Set getPropertyNames(PropertyScope scope) {
		return Collections.unmodifiableSet(getScopedProperties(scope).keySet());
	}

	protected void checkScopeForWriteAccess(PropertyScope scope) {
		if (scope == null || PropertyScope.REQUEST.equals(scope)) {
			throw new IllegalArgumentException(
					"Scope is invalid for writing properties: " + scope);
		}
	}

	public Object getProperty(String key, Object defaultValue) {
		Object value = getProperty(key);
		if (value == null) {
			value = defaultValue;
		}
		return value;
	}

	public byte getByteProperty(String name, byte defaultValue) {
		return ObjectUtils.getByte(getProperty(name), defaultValue);
	}

	public short getShortProperty(String name, short defaultValue) {
		return ObjectUtils.getShort(getProperty(name), defaultValue);
	}

	public int getIntProperty(String name, int defaultValue) {
		return ObjectUtils.getInt(getProperty(name), defaultValue);
	}

	public long getLongProperty(String name, long defaultValue) {
		return ObjectUtils.getLong(getProperty(name), defaultValue);
	}

	public float getFloatProperty(String name, float defaultValue) {
		return ObjectUtils.getFloat(getProperty(name), defaultValue);
	}

	public double getDoubleProperty(String name, double defaultValue) {
		return ObjectUtils.getDouble(getProperty(name), defaultValue);
	}

	public boolean getBooleanProperty(String name, boolean defaultValue) {
		return ObjectUtils.getBoolean(getProperty(name), defaultValue);
	}

	public String getStringProperty(String name, String defaultValue) {
		return ObjectUtils.getString(getProperty(name), defaultValue);
	}

	public String toString() {
		StringBuffer buf = new StringBuffer(128);
		buf.append("Properites{");
		for (Iterator iterator = scopedMap.entrySet().iterator(); iterator
				.hasNext();) {
			Map.Entry entry = (Map.Entry) iterator.next();
			buf.append(entry.getKey()).append(":");
			buf.append(MapUtils.toString((Map) entry.getValue(), false));
			buf.append("");
		}
		buf.append("}");
		return buf.toString();
	}
}
